package com.replication.jdbc.connection;

import com.replication.jdbc.statement.ReplicationStatementProxy;
import com.replication.jdbc.statement.Statements;

import java.sql.*;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

public class ReplicationConnectionProxy implements Connection {
    private final Connection masterConn;
    private final List<Connection> slaverConns;
    private Iterator<Connection> slaverIt;
    private ConnectionSelector selector;

    public ReplicationConnectionProxy(Connection masterConn, List<Connection> slaverConns, ConnectionSelector selector) {
        this.masterConn = masterConn;
        this.slaverConns = slaverConns;
        this.selector = selector;
    }

    public ConnectionSelector getConnectionSelector() {
        return this.selector;
    }

    public Connection getMasterConnection() {
        return this.masterConn;
    }

    protected synchronized Connection nextSlaverConnection() {
        if(this.slaverIt == null || !this.slaverIt.hasNext()) {
            this.slaverIt = this.slaverConns.iterator();
        }

        return this.slaverIt.hasNext() ? this.slaverIt.next() : this.masterConn;
    }

    public void clearWarnings() throws SQLException {
        this.masterConn.clearWarnings();
    }

    public void close() throws SQLException {
        this.closeConnection(this.masterConn);
        Iterator iterator = this.slaverConns.iterator();

        while(iterator.hasNext()) {
            Connection slavConn = (Connection)iterator.next();
            this.closeConnection(slavConn);
        }

    }

    private void closeConnection(Connection conn) {
        try {
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void commit() throws SQLException {
        this.masterConn.commit();
    }

    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        return this.masterConn.createArrayOf(typeName, elements);
    }

    public Blob createBlob() throws SQLException {
        return this.masterConn.createBlob();
    }

    public Clob createClob() throws SQLException {
        return this.masterConn.createClob();
    }

    public NClob createNClob() throws SQLException {
        return this.masterConn.createNClob();
    }

    public SQLXML createSQLXML() throws SQLException {
        return this.masterConn.createSQLXML();
    }

    public Statement createStatement() throws SQLException {
        return new ReplicationStatementProxy(this);
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        return new ReplicationStatementProxy(this, resultSetType, resultSetConcurrency);
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return new ReplicationStatementProxy(this, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        return this.masterConn.createStruct(typeName, attributes);
    }

    public void setSchema(String schema) throws SQLException {

    }

    public String getSchema() throws SQLException {
        return null;
    }

    public void abort(Executor executor) throws SQLException {

    }

    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {

    }

    public int getNetworkTimeout() throws SQLException {
        return 0;
    }

    public boolean getAutoCommit() throws SQLException {
        return this.masterConn.getAutoCommit();
    }

    public String getCatalog() throws SQLException {
        return this.masterConn.getCatalog();
    }

    public Properties getClientInfo() throws SQLException {
        return this.masterConn.getClientInfo();
    }

    public String getClientInfo(String name) throws SQLException {
        return this.masterConn.getClientInfo(name);
    }

    public int getHoldability() throws SQLException {
        return this.masterConn.getHoldability();
    }

    public DatabaseMetaData getMetaData() throws SQLException {
        return this.masterConn.getMetaData();
    }

    public int getTransactionIsolation() throws SQLException {
        return this.masterConn.getTransactionIsolation();
    }

    public Map<String, Class<?>> getTypeMap() throws SQLException {
        return this.masterConn.getTypeMap();
    }

    public SQLWarning getWarnings() throws SQLException {
        return this.masterConn.getWarnings();
    }

    public boolean isClosed() throws SQLException {
        return this.masterConn.isClosed();
    }

    public boolean isReadOnly() throws SQLException {
        return this.masterConn.isReadOnly();
    }

    public boolean isValid(int timeout) throws SQLException {
        return this.masterConn.isValid(timeout);
    }

    public String nativeSQL(String sql) throws SQLException {
        return this.masterConn.nativeSQL(sql);
    }

    public CallableStatement prepareCall(String sql) throws SQLException {
        return this.masterConn.prepareCall(sql);
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return this.masterConn.prepareCall(sql, resultSetType, resultSetConcurrency);
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return this.masterConn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return Statements.prepareStatement(this, sql);
    }

    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        return Statements.prepareStatement(this, sql, autoGeneratedKeys);
    }

    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        return Statements.prepareStatement(this, sql, columnIndexes);
    }

    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        return Statements.prepareStatement(this, sql, columnNames);
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return Statements.prepareStatement(this, sql, resultSetType, resultSetConcurrency);
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return Statements.prepareStatement(this, sql, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        this.masterConn.releaseSavepoint(savepoint);
    }

    public void rollback() throws SQLException {
        this.masterConn.rollback();
    }

    public void rollback(Savepoint savepoint) throws SQLException {
        this.masterConn.releaseSavepoint(savepoint);
    }

    public void setAutoCommit(boolean autoCommit) throws SQLException {
        this.masterConn.setAutoCommit(autoCommit);
    }

    public void setCatalog(String catalog) throws SQLException {
        this.masterConn.setCatalog(catalog);
    }

    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        this.masterConn.setClientInfo(properties);
    }

    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        this.masterConn.setClientInfo(name, value);
    }

    public void setHoldability(int holdability) throws SQLException {
        this.masterConn.setHoldability(holdability);
    }

    public void setReadOnly(boolean readOnly) throws SQLException {
        this.masterConn.setReadOnly(readOnly);
    }

    public Savepoint setSavepoint() throws SQLException {
        return this.masterConn.setSavepoint();
    }

    public Savepoint setSavepoint(String name) throws SQLException {
        return this.masterConn.setSavepoint(name);
    }

    public void setTransactionIsolation(int level) throws SQLException {
        this.masterConn.setTransactionIsolation(level);
    }

    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
        this.masterConn.setTypeMap(map);
    }

    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return this.masterConn.isWrapperFor(iface);
    }

    public <T> T unwrap(Class<T> iface) throws SQLException {
        return this.masterConn.unwrap(iface);
    }

}
